package com.yinhanshi.s3test.upload;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.AbortMultipartUploadRequest;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest;
import com.amazonaws.services.s3.model.CompleteMultipartUploadResult;
import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
import com.amazonaws.services.s3.model.InitiateMultipartUploadResult;
import com.amazonaws.services.s3.model.PartETag;
import com.amazonaws.services.s3.model.ProgressEvent;
import com.amazonaws.services.s3.model.ProgressListener;
import com.amazonaws.services.s3.model.UploadPartRequest;
import com.amazonaws.services.s3.model.UploadPartResult;
import com.yinhanshi.s3test.utils.SharedPreferencesCompat;
import com.yinhanshi.s3test.utils.SharedPreferencesUtils;

public class Uploader {
	
	private static final long MIN_DEFAULT_PART_SIZE = 5 * 1024 * 1024; // 1M
	
	private static final String TAG = "Simpl3r";
	private static final String PREFS_NAME = "preferences_simpl3r";
	private static final String PREFS_UPLOAD_ID = "_uploadId";
	private static final String PREFS_ETAGS = "_etags";
	private static final String PREFS_ETAG_SEP = "~~";
	
	private AmazonS3Client s3Client;
	private String s3bucketName;
	private String s3key;
	private File file;
	
	private SharedPreferences prefs;
	private long partSize = MIN_DEFAULT_PART_SIZE;	
	private UploadProgressListener progressListener;
	private long bytesUploaded = 0;
	private boolean userInterrupted = false;
	private boolean userAborted = false;
	
	public Uploader(Context context, AmazonS3Client s3Client, String s3bucketName, String s3key, File file) {
		this.s3Client = s3Client;
		this.s3key = s3key;
		this.s3bucketName = s3bucketName;
		this.file = file;
		prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
	}
	
	/**
	 * 将文件以分片的形式上传到S3
	 * 
	 * @return 文件在S3中的url
	 */
	public String start() {
		
		// 初始化
		List<PartETag> partETags = new ArrayList<PartETag>();
		final long contentLength = file.length();
		long filePosition = 0;
		int startPartNumber = 1;
		
		userInterrupted = false;
		userAborted = false;
		bytesUploaded = 0;
		
		// 是否有未完成的任务
		String uploadId = getCachedUploadId();
		
		if (uploadId != null) {
			// 重新上传
			Log.i(TAG, "resuming upload for " + uploadId);
			
			// 获取缓存的 etags
			List<PartETag> cachedEtags = getCachedPartEtags();
			partETags.addAll(cachedEtags);
						
			// 计算起始位置
			startPartNumber = cachedEtags.size() + 1;
			filePosition = (startPartNumber -1) * partSize;
			bytesUploaded = filePosition;
			
			Log.i(TAG, "resuming at part " + startPartNumber + " position " + filePosition);
		
		} else {
			// 启动新的上传
			Log.i(TAG, "initiating new upload");
			
	        InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(s3bucketName, s3key);
	        configureInitiateRequest(initRequest);
	        InitiateMultipartUploadResult initResponse = s3Client.initiateMultipartUpload(initRequest);
	        uploadId = initResponse.getUploadId();
			
		}
		
		final AbortMultipartUploadRequest abortRequest = new AbortMultipartUploadRequest(s3bucketName, s3key, uploadId);
        
        for (int k = startPartNumber; filePosition < contentLength; k++) {
        	//当前分片总长度
            long thisPartSize = Math.min(partSize, (contentLength - filePosition));
            
            Log.i(TAG, "starting file part " + k + " with size " + thisPartSize);
            
            UploadPartRequest uploadRequest = new UploadPartRequest()
					.withBucketName(s3bucketName) //桶
                    .withKey(s3key)				  //s3key
					.withUploadId(uploadId)		  //上传ID
                    .withPartNumber(k)			  //第几个分片
					.withFileOffset(filePosition) //起始位置
					.withFile(file)				  //文件路径
                    .withPartSize(thisPartSize);  //当前分片长度

            ProgressListener s3progressListener = new ProgressListener() {
                public void progressChanged(ProgressEvent progressEvent) {
                	// 用户暂停
                    if (userInterrupted) {
                		s3Client.shutdown(); 
                		throw new UploadIterruptedException("User interrupted");
                	} else if (userAborted) {
                		// 用户取消，所以将本地记录清空
                		clearProgressCache();
                    	s3Client.abortMultipartUpload(abortRequest);
                    	s3Client.shutdown();
                    }
                    
                    bytesUploaded += progressEvent.getBytesTransfered();
                    
                    //Log.d(TAG, "bytesUploaded=" + bytesUploaded);
                    
                    // 计算当前进度，并发送通知
                    float fpercent = ((bytesUploaded * 100) / contentLength);
                    int percent = Math.round(fpercent);
                    if (progressListener != null) {
                    	progressListener.progressChanged(progressEvent, bytesUploaded, percent);
                    }
                    
                }
            };
            
            uploadRequest.setProgressListener(s3progressListener);
            
            UploadPartResult result = s3Client.uploadPart(uploadRequest);
            
            partETags.add(result.getPartETag());
            
            // 缓存 uploadId
            if (k == 1) {
            	initProgressCache(uploadId);
            }
            // 缓存分片 etag
            cachePartEtag(result);
            
            filePosition += thisPartSize;
        }

        //全部分片上传完成
        CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(
        		s3bucketName, s3key, uploadId,
                partETags);
        CompleteMultipartUploadResult result = s3Client.completeMultipartUpload(compRequest);
        bytesUploaded = 0;
        
        Log.i(TAG, "upload complete for " + uploadId);
        
        clearProgressCache();
 
        return result.getLocation();
		
	}

	private String getCachedUploadId() {
		return prefs.getString(s3key + PREFS_UPLOAD_ID, null);
	}

	/**
	 * 获取缓存的 etags
	 * @return  etags
	 */
	private List<PartETag> getCachedPartEtags() {
		List<PartETag> result = new ArrayList<PartETag>();		
		// 获取缓存的 etags
		ArrayList<String> etags = SharedPreferencesUtils.getStringArrayPref(prefs, s3key + PREFS_ETAGS);
		for (String etagString : etags) {
			String partNum = etagString.substring(0, etagString.indexOf(PREFS_ETAG_SEP));
			String partTag = etagString.substring(etagString.indexOf(PREFS_ETAG_SEP) + 2, etagString.length());
						
			PartETag etag = new PartETag(Integer.parseInt(partNum), partTag);
			result.add(etag);
		}
		return result;
	}

	/**
	 * 将当前分片的 ETag 添加到缓存中
	 * @param result S3返回的分片 Result
	 */
	private void cachePartEtag(UploadPartResult result) {
		String serialEtag = result.getPartETag().getPartNumber() + PREFS_ETAG_SEP + result.getPartETag().getETag();
		ArrayList<String> etags = SharedPreferencesUtils.getStringArrayPref(prefs, s3key + PREFS_ETAGS);
		etags.add(serialEtag);
		SharedPreferencesUtils.setStringArrayPref(prefs, s3key + PREFS_ETAGS, etags);
	}

	private void initProgressCache(String uploadId) {
		// 缓存 uploadId
		Editor edit = prefs.edit().putString(s3key + PREFS_UPLOAD_ID, uploadId);
		SharedPreferencesCompat.apply(edit);
		// 创建一个空的 etag array
		ArrayList<String> etags = new ArrayList<String>();
		SharedPreferencesUtils.setStringArrayPref(prefs, s3key + PREFS_ETAGS, etags);
	}

	/**
	 * 清空缓存的 uploadId and etags
	 */
	private void clearProgressCache() {
        Editor edit = prefs.edit();
        edit.remove(s3key + PREFS_UPLOAD_ID);
        edit.remove(s3key + PREFS_ETAGS);
    	SharedPreferencesCompat.apply(edit);
	}
	
	public void interrupt() {
		userInterrupted = true;
	}
	
	public void abort() {
		userAborted = true;
	}
	
	/**
	 * 设置文件的访问权限为 PublicRead.
	 * 
	 * @param initRequest 上传文件的 S3 request object
	 */
	protected void configureInitiateRequest(InitiateMultipartUploadRequest initRequest) {
		initRequest.setCannedACL(CannedAccessControlList.PublicRead);
	}
	
	public void setPrefs(SharedPreferences prefs) {
		this.prefs = prefs;
	}

	/**
	 * 获取设定的分片大小
	 * @return 分片大小
	 */
	public long getPartSize() {
		return partSize;
	}

	/**
	 * 设定分片大小
	 * @param partSize 分片大小
	 */
	public void setPartSize(long partSize) {
		if (partSize < MIN_DEFAULT_PART_SIZE) {
			throw new IllegalStateException("Part size is less than S3 minimum of " + MIN_DEFAULT_PART_SIZE);
		} else {
			this.partSize = partSize;
		}	
	}
	
	public void setProgressListener(UploadProgressListener progressListener) {
		this.progressListener = progressListener;
	}

	public interface UploadProgressListener {
		public void progressChanged(ProgressEvent progressEvent, long bytesUploaded, int percentUploaded);
	}
	
}
